/*
let const
ES5没有块级作用域，导致变量声明使用后容易变成全局变量造成污染
使用let和const将变量声明为块级作用域中的变量，变量不会进行声明提升
 */
{
  let a = 1;
  var b = 2;
}
// console.log(a);  //报错
console.log(b);

/*
ES6 允许按照一定模式，从数组和对象中提取值，对变量进行赋值，这被称为解构
 */
//1.交换变量的值
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x);

//2.从函数中返回多个值
function foo() {
  return [1, 2, 3];
}

let [t1, t2, t3] = foo();
console.log(t3);

//3.函数参数的定义
function f([x, y, z]) {
  console.log(z);
}

f([1, 2, 3]);
//4.提取 JSON 数据
let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};
let {id, status, data: number} = jsonData;
console.log(id, status, number);
//5.函数参数的默认值
//6.遍历 Map 结构
//7.输入模块的指定方法

/*
模板字符串是增强版字符串，使用``标示，使用${}插入变量，变量可以计算，可以是调用函数，可以嵌套
 */
var abc = 1;
var cba = 2;
console.log(`hello world ${abc}`);
console.log(`hello world ${abc * 5 + cba}`);
console.log(`hello world ${f([1, 2, 9])}`);

/*
函数扩展
 */

//1.函数参数默认值，可以配合解构赋值使用
function aoo(a = 0) {
  console.log(a);
}

aoo();

//2.rest参数，利用 rest 参数，可以向该函数传入任意数目的参数
function add(...values) {
  let sum = 0;
  for (let val of values) {
    sum += val;
  }
  console.log(sum);
}

add(1, 2, 3);
add(1, 2);
//3.箭头函数有几个使用注意点:
//函数体内的this对象，就是定义时所在的对象，而不是使用时所在的对象。
//不可以当作构造函数，也就是说，不可以使用new命令，否则会抛出一个错误。
//不可以使用arguments对象，该对象在函数体内不存在。如果要用，可以用 rest 参数代替。
//不可以使用yield命令，因此箭头函数不能用作 Generator 函数。
var boo = (num1, num2) => num1 + num2;
console.log(boo(3, 2));

/*
ES6中引入新的原始数据类型symbol，使用构造函数生成，代表一个独一无二的值
 */
var s1 = Symbol("soo");
var s2 = Symbol("soo");
console.log(s1 == s2);
var f1 = Symbol.for("foo");
var f2 = Symbol.for("foo");
console.log(f1 == f2);

/*
Set类似数组，没有重复数据，add/delete/has/clear进行操作，使用keys/values/entries/forEach进行遍历
Map类似JAVA中的Map，采用key-value的形式保存数据，key可以是一个对象，set/get/delete/has/size来操作，遍历同Set
 */
var set1 = new Set([1, 1, 2, 2, 3]);
var set1Arr = [...set1];  //扩展运算符（...）内部使用for...of循环
console.log(set1Arr);
var map1 = new Map();
var o = {foo: "foo"};
map1.set(o, "content");
console.log(map1.get(o));
map1.forEach((value, key) => {
  console.log(key, value);
});

/*
Proxy可以理解为在访问一个对象前添加一层拦截器，因此提供了一种机制，可以对外界的访问进行过滤和改写
Proxy构造函数两个参数，target参数表示所要拦截的目标对象，handler参数也是一个对象，用来定制拦截行为
Proxy可对多个方法进行拦截，包括has/deleteProperty/ownKeys/defineProperty等，拦截操作的参数在手机截图上
 */
var obj = new Proxy({}, {
  //target目标对象，key属性名称，receiver为proxy实例本身
  get: function (target, key, receiver) {
    console.log(`get ${key}`);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, value, receiver) {
    console.log(`set ${key}`);
    return Reflect.set(target, key, value, receiver);
  }
});
obj.count = 1;
++obj.count;

/*
Iterator 接口的目的，就是为所有数据结构，提供了一种统一的访问机制.
当使用for...of循环遍历某种数据结构时，该循环会自动去寻找 Iterator 接口。
一种数据结构只要部署了 Iterator 接口，我们就称这种数据结构是“可遍历的”（iterable）。
ES6 规定，默认的 Iterator 接口部署在数据结构的Symbol.iterator属性，或者说，一个数据结构只要具有Symbol.iterator属性，
就可以认为是“可遍历的”（iterable）。Symbol.iterator属性本身是一个函数，就是当前数据结构默认的遍历器生成函数。
执行这个函数，就会返回一个遍历器。
原生具备 Iterator 接口的数据结构：Array/Map/Set/String/TypedArray/函数的 arguments 对象/NodeList 对象
 */
const obj1 = {
  [Symbol.iterator]: function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true
        };
      }
    };
  }
};
console.log(obj1[Symbol.iterator]().next().value);

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
console.log(iter.next()); // { value: 'a', done: false }
console.log(iter.next()); // { value: 'b', done: false }
console.log(iter.next()); // { value: 'c', done: false }
console.log(iter.next()); // { value: undefined, done: true }
for (let va of arr) {
  console.log(va);
}

/*
Generator 函数是 ES6 提供的一种异步编程解决方案，语法行为与传统函数完全不同
Generator 函数有多种理解角度。语法上，首先可以把它理解成，Generator 函数是一个状态机，封装了多个内部状态
执行 Generator 函数会返回一个遍历器对象，也就是说，Generator 函数除了状态机，还是一个遍历器对象生成函数。
返回的遍历器对象，可以依次遍历 Generator 函数内部的每一个状态
形式上，Generator 函数是一个普通函数，但是有两个特征。一是，function关键字与函数名之间有一个星号；
二是，函数体内部使用yield表达式，定义不同的内部状态（yield在英语里的意思就是“产出”）。
Generator 函数的调用方法与普通函数一样，也是在函数名后面加上一对圆括号。
不同的是，调用 Generator 函数后，该函数并不执行，返回的也不是函数运行结果，而是一个指向内部状态的指针对象
总结一下，调用 Generator 函数，返回一个遍历器对象，代表 Generator 函数的内部指针。
以后，每次调用遍历器对象的next方法，就会返回一个有着value和done两个属性的对象。
value属性表示当前的内部状态的值，是yield表达式后面那个表达式的值；done属性是一个布尔值，表示是否遍历结束
由于 Generator 函数就是遍历器生成函数，因此可以把 Generator 赋值给对象的Symbol.iterator属性，从而使得该对象具有 Iterator 接口
 */
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();
console.log(hw.next());
console.log(hw.next());
console.log(hw.next());
console.log(hw.next());

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable] // [1, 2, 3]